﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Cuckoo.TaskSchedule
{
	/// <summary>
	/// 特定时间执行的任务处理策略
	/// </summary>
	internal class FixedTaskStrategy : ITaskStrategy
	{
		private FixedTaskStrategy() { }

		private static FixedTaskStrategy _Instance;
		public static FixedTaskStrategy Instance
		{
			get
			{
				if (_Instance == null)
				{
					_Instance = new FixedTaskStrategy();
				}
				return _Instance;
			}
		}

		/// <summary>
		/// 注册任务
		/// </summary>
		/// <param name="task">待注册任务</param>
		public void RegisterTask(ITask task)
		{
            task.Timer = TimerFactory.CreateTimer(task);
			ParseExpress(task, task.Timer);
            (task.Timer as IFixedTimer).Task = task;
            task.Timer.SecondTrigger += task.Work;
		}

		/// <summary>
		/// 注销任务
		/// </summary>
		/// <param name="task">待注销任务</param>
		public void UnRegisterTask(ITask task)
		{
			TimerFactory.GetBaseTimer().SecondTrigger -= task.Timer.OnSecondTrigger;
			task.Timer.SecondTrigger -= task.Work;
		}

		/// <summary>
		/// 解析任务的时间表达式
		/// </summary>
		/// <param name="task">待解析的任务</param>
		/// <returns>解析结果</returns>
		public void ParseExpress(ITask task, ITimer timer)
		{
            IFixedTimer time = timer as IFixedTimer;
			string strExpSub1 = string.Empty, strExpSub2 = string.Empty;
			int dollerpos = task.ScheduleExpress.IndexOf('$');
			int andpos = task.ScheduleExpress.IndexOf('&');
			if(dollerpos == -1)
			{
				throw new ExpressionException();
			}
			if(andpos == -1)
			{
				strExpSub1 = task.ScheduleExpress.Substring(dollerpos + 1, 
					task.ScheduleExpress.Length - dollerpos - 1);
			}
			else
			{
				strExpSub1 = task.ScheduleExpress.Substring(dollerpos + 1, 
					task.ScheduleExpress.Length - andpos - dollerpos + 1);
				strExpSub2 = task.ScheduleExpress.Substring(andpos + 1);
			}

            time.nRunYear = TaskStrategyHelper.GetSection(strExpSub1, "Y", true);
            time.nRunMonth = TaskStrategyHelper.GetSection(strExpSub1, "M", true);
            time.nRunDate = TaskStrategyHelper.GetSection(strExpSub1, "D", true);
            time.nRunHour = TaskStrategyHelper.GetSection(strExpSub1, "H", true);
            time.nRunMinute = TaskStrategyHelper.GetSection(strExpSub1, "m", true);
            time.nRunSecond = TaskStrategyHelper.GetSection(strExpSub1, "s", true);
            time.nRunNum = TaskStrategyHelper.GetSection(strExpSub1, "n", true);
            time.nRunDay = TaskStrategyHelper.GetSection(strExpSub1, "d", true);

            int hour = TaskStrategyHelper.GetSection(strExpSub2, "H", true);
            int minute = TaskStrategyHelper.GetSection(strExpSub2, "m", true);
            int second = TaskStrategyHelper.GetSection(strExpSub2, "s", true);
			time.Interval =  second + minute * 60 + hour * 3600;
            time.nMaxCount = TaskStrategyHelper.GetSection(task.ScheduleExpress, "c", true);
			time.FixedType = GetFixedType(time.nRunYear, time.nRunMonth, time.nRunDate, time.nRunNum, time.nRunDay);
            CheckExpressValid(time);
		}

        private void CheckExpressValid(IFixedTimer timer)
        {
            //检验时间的有效性
            if((timer.nRunHour < 0 || timer.nRunHour > 23) ||
                (timer.nRunMinute < 0 || timer.nRunMinute > 59)||
                (timer.nRunSecond < 0 || timer.nRunSecond > 59))
            {
                throw new ExpressionException("表达式中时间值超出有效范围！");
            }
            //检验日期的有效性
            switch (timer.FixedType)
            {
                case FixedType.FIXED_ERROR: throw new ExpressionException("表达式格式错误！");
                case FixedType.EVERYDAY_RUN: break;
                case FixedType.FIXEDYEAR_EVERYDAY_RUN:
                    if (timer.nRunYear < DateTime.Now.Year)
                    {
                        throw new ExpressionException("表达式无效，该年份值小于当前年，无法执行任务！");
                    }
                    break;
                case FixedType.FIXMONTH_EVERYDAY_RUN:
                    if (timer.nRunMonth < 1 || timer.nRunMonth > 12)
                    {
                        throw new ExpressionException("表达式无效，月份值超出有效范围，有效值[1~12]！");
                    }
                    break;
                case FixedType.FIXEDYEAR_FIXEDMONTH_EVERYDAY_RUN:
                    if (timer.nRunYear < DateTime.Now.Year)
                    {
                        throw new ExpressionException("表达式无效，该年份值小于当前年，无法执行任务！");
                    }
                    if (timer.nRunMonth < 1 || timer.nRunMonth > 12)
                    {
                        throw new ExpressionException("表达式无效，月份值超出有效范围，有效值[1~12]！");
                    }
                    if (timer.nRunYear == DateTime.Now.Year && timer.nRunMonth < DateTime.Now.Month)
                    {
                        throw new ExpressionException("表达式无效，该月份值小于当前月，无法执行任务！");
                    }
                    break;
                case FixedType.EVERYMONTH_FIXEDDAY_RUN:
                    if (timer.nRunDate < 1 || timer.nRunDate > 31)
                    {
                        throw new ExpressionException("表达式无效，日期超出范围，D取值范围：[1~31]！");
                    }
                    break;
                case FixedType.FIXEDYEAR_EVERYMONTH_FIXEDDAY_RUN:
                    if (timer.nRunYear < DateTime.Now.Year)
                    {
                        throw new ExpressionException("表达式无效，该年份值小于当前年，无法执行任务！");
                    }
                    if (timer.nRunDate < 1 || timer.nRunDate > 31)
                    {
                        throw new ExpressionException("表达式无效，日期超出范围，D取值范围：[1~31]！");
                    }
                    break;
                case FixedType.FIXEDYEAR_FIXEDDAY_RUN:
                    {
                        if (timer.nRunYear < DateTime.Now.Year)
                        {
                            throw new ExpressionException("表达式无效，该年份值小于当前年，无法执行任务！");
                        }
                        DateTime time = new DateTime(timer.nRunYear, 1, 1);
                        DateTime time2 = time.AddYears(1);
                        if (timer.nRunDate < 1 || timer.nRunDate > (time2 - time).TotalDays)
                        {
                            throw new ExpressionException("表达式无效，指定天超出了该年的有效天数！");
                        }
                    }
                    break;
                case FixedType.FIXEDYEAR_FIXEDMONTH_FIXEDDAY_RUN:
                    {
                        try
                        {
                            DateTime time = new DateTime(timer.nRunYear, timer.nRunMonth, timer.nRunDate);
                            if (time < DateTime.Now)
                            {
                                throw new ExpressionException("表达式无效，指定的日期值小于当前日期，无法执行任务！");
                            }
                        }
                        catch (Exception ex)
                        {
                            throw new ExpressionException("表达式无效，指定的日期值不合法！");
                        }
                    }
                    break;
                case FixedType.EVERYYEAR_EVERYMONTH_FIXEDWEEKDAY_RUN: break;
                case FixedType.EVERYYEAR_FIXEDMONTH_FIXEDWEEKDAY_RUN: break;
                case FixedType.FIXEDYEAR_EVERYMONTH_FIXEDWEEKDAY_RUN: break;
                case FixedType.FIXEDYEAR_FIXEDMONTH_FIXEDWEEKDAY_RUN: break;
                case FixedType.EVERYYEAR_FIXEDWEEKDAY_RUN: break;
                case FixedType.FIXEDYEAR_FIXEDWEEKDAY_RUN: break;
                case FixedType.EVERYYEAR_EVERYMONTH_EVERYWEEKDAY_RUN: break;
                case FixedType.EVERYYEAR_FIXEDMONTH_EVERYWEEKDAY_RUN: break;
                case FixedType.FIXEDYEAR_EVERYMONTH_EVERYWEEKDAY_RUN: break;
                case FixedType.FIXEDYEAR_FIXEDMONTH_EVERYWEEKDAY_RUN: break;
                case FixedType.EVERYYEAR_EVERYWEEKDAY_RUN: break;
                case FixedType.FIXEDYEAR_EVERYWEEKDAY_RUN: break;
            }
        }

		private FixedType GetFixedType(int year, int month, int date, int num, int day)
		{
			FixedType type = FixedType.FIXED_ERROR;

			if(date == -1 && (month == -1 || month == 0) && ( year == 0 || year == -1))
			{
				type = FixedType.EVERYDAY_RUN;
			}
			else if (date == -1 && (month == -1 || month == 0) && year > 0)
			{
				type = FixedType.FIXEDYEAR_EVERYDAY_RUN;
			}
			else if (date == -1 && month > 0 && (year == 0 || year == -1))
			{
				type = FixedType.FIXMONTH_EVERYDAY_RUN;
			}
			else if (date == -1 && month > 0 && year > 0)
			{
				type = FixedType.FIXEDYEAR_FIXEDMONTH_EVERYDAY_RUN;
			}
			else if (date > 0 && month == -1 && (year == 0 || year == -1))
			{
				type = FixedType.EVERYMONTH_FIXEDDAY_RUN;
			}
			else if (date > 0 && month == -1 && year > 0)
			{
				type = FixedType.FIXEDYEAR_EVERYMONTH_FIXEDDAY_RUN;
			}
            else if (date > 0 && month == 0 && year > 0)
            {
                type = FixedType.FIXEDYEAR_FIXEDDAY_RUN;
            }
            else if (date > 0 && month > 0 && year > 0)
            {
                type = FixedType.FIXEDYEAR_FIXEDMONTH_FIXEDDAY_RUN;
            }
            else if (num > 0 && (day > 0 && day < 8) && month == -1 && (year == 0 || year == -1))
            {
                type = FixedType.EVERYYEAR_EVERYMONTH_FIXEDWEEKDAY_RUN;
            }
            else if (num > 0 && (day > 0 && day < 8) && month >0 && (year == 0 || year == -1))
            {
                type = FixedType.EVERYYEAR_FIXEDMONTH_FIXEDWEEKDAY_RUN;
            }
            else if (num > 0 && (day > 0 && day < 8) && month == -1 && year > 0)
            {
                type = FixedType.FIXEDYEAR_EVERYMONTH_FIXEDWEEKDAY_RUN;
            }
            else if (num > 0 && (day > 0 && day < 8) && month > 0 && year > 0)
            {
                type = FixedType.FIXEDYEAR_FIXEDMONTH_FIXEDWEEKDAY_RUN;
            }
            else if (num > 0 && (day > 0 && day < 8) && month == 0 && (year == 0 || year == -1))
            {
                type = FixedType.EVERYYEAR_FIXEDWEEKDAY_RUN;
            }
            else if (num > 0 && (day > 0 && day < 8) && month == 0 && year > 0)
            {
                type = FixedType.FIXEDYEAR_FIXEDWEEKDAY_RUN;
            }
            else if ((day > 0 && day < 8) && month == -1 && (year == 0 || year == -1))
            {
                type = FixedType.EVERYYEAR_EVERYMONTH_EVERYWEEKDAY_RUN;
            }
            else if ((day > 0 && day < 8) && month > 0 && (year == 0 || year == -1))
            {
                type = FixedType.EVERYYEAR_FIXEDMONTH_EVERYWEEKDAY_RUN;
            }
            else if ((day > 0 && day < 8) && month == -1 && year > 0)
            {
                type = FixedType.FIXEDYEAR_EVERYMONTH_EVERYWEEKDAY_RUN;
            }
            else if ((day > 0 && day < 8) && month > 0 && year > 0)
            {
                type = FixedType.FIXEDYEAR_FIXEDMONTH_EVERYWEEKDAY_RUN;
            }
            else if ((day > 0 && day < 8) && month == 0 && (year == 0 || year == -1))
            {
                type = FixedType.EVERYYEAR_EVERYWEEKDAY_RUN;
            }
            else if ((day > 0 && day < 8) && month == 0 && year > 0)
            {
                type = FixedType.FIXEDYEAR_EVERYWEEKDAY_RUN;
            }
			return type;
		}
	}
}
